package t

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	ACCOUNTMODEL "googee/service/account/model"
	ADDRBOOK "googee/service/addrbook/model"
	INBOX "googee/service/inbox/model"
	LEADERMODEL "googee/service/leaderboard/model"
	SMSMODEL "googee/service/sms/model"

	TASKMODEL "googee/service/task/model"
	USERMODEL "googee/service/user/model"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/zeromicro/go-zero/core/conf"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

type Config struct {
	Mysql struct {
		DataSource string
	}

	CacheRedis cache.CacheConf

	API string

	Redis struct {
		Host string
		Pass string
		DB   int64
	}
	MeiliSearchServer struct {
		Host   string
		APIKey string
	}
}

var c Config

func init() {
	rand.Seed(time.Now().UnixNano())
	logx.Disable()
	str, _ := os.Getwd()
	filename := path.Base(str)
	if filename != "googee" {
		conf.MustLoad(filepath.Join(str, "..", "conf.yaml"), &c)
	} else {
		conf.MustLoad(filepath.Join(str, "conf.yaml"), &c)
	}
}

func GetConfig() *Config {
	return &c
}

func ClearDb() {
	// fmt.Println("ClearDb")
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// fmt.Println("连接数据库")
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	{
		var m ACCOUNTMODEL.AccountModel = ACCOUNTMODEL.NewAccountModel(conn, c.CacheRedis)
		m.Truncate(ctx)

	}

	// color.Red("删除用户数据库")
	//删除用户数据库
	{
		var user USERMODEL.UserModel = USERMODEL.NewUserModel(conn, c.CacheRedis)
		user.Truncate(ctx)

		var profile USERMODEL.ProfileModel = USERMODEL.NewProfileModel(conn, c.CacheRedis)
		profile.Truncate(ctx)
	}

	// color.Red("删除短信数据库")
	//删除短信数据库
	{
		var sms SMSMODEL.SmsModel = SMSMODEL.NewSmsModel(conn, c.CacheRedis)
		sms.Truncate(ctx)
	}

	// color.Red("删除排行榜数据")
	//删除排行榜数据
	{
		{
			var m LEADERMODEL.LeaderboardsModel = LEADERMODEL.NewLeaderboardsModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}
		{
			var m LEADERMODEL.LeaderboardsDataModel = LEADERMODEL.NewLeaderboardsDataModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}
		{
			var m LEADERMODEL.LeaderboardsBucketsModel = LEADERMODEL.NewLeaderboardsBucketsModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}
	}

	// color.Red("删除地址簿")
	//addrbook
	{
		{
			var m ADDRBOOK.AddrbookModel = ADDRBOOK.NewAddrbookModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}
	}

	// color.Red("收件箱数据")
	//inbox
	{
		{
			var m INBOX.InboxMessageModel = INBOX.NewInboxMessageModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}

	}
	// color.Red("删除任务数据")
	//删除任务数据
	{
		{
			var m TASKMODEL.TaskModel = TASKMODEL.NewTaskModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}

		{
			var m TASKMODEL.ConditionConfigModel = TASKMODEL.NewConditionConfigModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}

		{
			var m TASKMODEL.TaskConditionModel = TASKMODEL.NewTaskConditionModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}

		{
			var m TASKMODEL.TaskBonusModel = TASKMODEL.NewTaskBonusModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}

		{
			var m TASKMODEL.TaskProgressModel = TASKMODEL.NewTaskProgressModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}

		{
			var m TASKMODEL.TaskGroupModel = TASKMODEL.NewTaskGroupModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}

		{
			var m TASKMODEL.TaskStatusModel = TASKMODEL.NewTaskStatusModel(conn, c.CacheRedis)
			m.Truncate(ctx)
		}
	}
	time.Sleep(1 * time.Second)
}

type registerReq struct {
	Username string `json:"username" validate:"required,min=4,max=8"`
	Password string `json:"password" validate:"required,min=6,max=8"`
}

type registerReply struct {
	Code int       `json:"code"`
	Msg  string    `json:"msg"`
	Data loginData `json:"data"`
}

type loginData struct {
	JWT  jWTData  `json:"jwt"`
	User userInfo `json:"user"`
}

type userInfo struct {
	Id       int64  `json:"id"`
	Name     string `json:"name"`
	Phone    string `json:"phone"`
	Gender   string `json:"gender"`
	Nickname string `json:"nickname"`
	Avatar   string `json:"avatar"`
	Uid      string `json:"uid"`
	Invcode  string `json:"invcode"`
}

type jWTData struct {
	AccessToken  string `json:"accessToken"`
	AccessExpire int64  `json:"accessExpire"`
	RefreshAfter int64  `json:"refreshAfter"`
}

func GetFromRedis(key string) string {
	c := GetConfig()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	client := redis.NewClient(&redis.Options{
		Addr:     c.Redis.Host,
		Password: c.Redis.Pass,
		DB:       0,   // use default DB
		PoolSize: 100, // 连接池大小
	})

	s, err := client.Get(ctx, key).Result()
	if err != nil {
		return ""
	}
	return s
}

func GetLoginJWT(name, password string) (string, int64) {
	var client = http.Client{}
	c := GetConfig()

	data := registerReq{
		Username: name,
		Password: password,
	}

	s, _ := json.Marshal(data)
	request, err := http.NewRequest("POST", c.API+"/api/user/login", bytes.NewBuffer(s))
	if err != nil {
		panic(err)
	}
	request.Header.Set("Content-Type", "application/json; charset=UTF-8")

	response, err := client.Do(request)
	if err != nil {
		return "", 0
	}
	defer response.Body.Close()
	body, _ := ioutil.ReadAll(response.Body)

	var resp registerReply
	if err := json.Unmarshal(body, &resp); err != nil {
		return "", 0
	}
	return resp.Data.JWT.AccessToken, resp.Data.User.Id
}

func GetRegisterJWT(name, password string) (string, int64) {
	var client = http.Client{}
	c := GetConfig()

	data := registerReq{
		Username: name,
		Password: password,
	}

	s, _ := json.Marshal(data)
	request, err := http.NewRequest("POST", c.API+"/api/user/register", bytes.NewBuffer(s))
	if err != nil {
		panic(err)
	}
	request.Header.Set("Content-Type", "application/json; charset=UTF-8")

	response, err := client.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := ioutil.ReadAll(response.Body)

	var resp registerReply
	if err := json.Unmarshal(body, &resp); err != nil {
		return "", 0
	}
	return resp.Data.JWT.AccessToken, resp.Data.User.Id
}

var headerNums = [...]string{"139", "138", "137", "136", "135", "134", "159", "158", "157", "150", "151", "152", "188", "187", "182", "183", "184", "178", "130", "131", "132", "156", "155", "186", "185", "176", "133", "153", "189", "180", "181", "177"}
var headerNumsLen = len(headerNums)

func RandomPhone() string {
	header := headerNums[rand.Intn(headerNumsLen)]
	body := fmt.Sprintf("%08d", rand.Intn(99999999))
	phone := header + body
	return phone
}

func FindLastOneCode(phone string) string {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	conn := sqlx.NewMysql(c.Mysql.DataSource)
	var sms SMSMODEL.SmsModel = SMSMODEL.NewSmsModel(conn, c.CacheRedis)
	s, err := sms.FindLastVerifySmsByPhone(ctx, phone)

	if err != nil {
		panic(err)
	}

	if s != nil {
		return s.Content.String
	}

	return ""
}

func InsertBoard(name string, typ string, desc string) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var board LEADERMODEL.LeaderboardsModel = LEADERMODEL.NewLeaderboardsModel(conn, c.CacheRedis)

	var model LEADERMODEL.Leaderboards = LEADERMODEL.Leaderboards{
		Name: name,
		Typ:  typ,
		Desc: desc,
	}

	_, err := board.Insert(ctx, &model)
	if err != nil {
		panic(err)
	}
}

func DelBoard(id int64) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var board LEADERMODEL.LeaderboardsModel = LEADERMODEL.NewLeaderboardsModel(conn, c.CacheRedis)

	board.Delete(ctx, id)
}

//插入需要考察的考核点的名称
func InsertConditionConfig(name string, desc string) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var model TASKMODEL.ConditionConfig = TASKMODEL.ConditionConfig{
		Name: name,
		Desc: desc,
	}

	var m TASKMODEL.ConditionConfigModel = TASKMODEL.NewConditionConfigModel(conn, c.CacheRedis)

	_, err := m.Insert(ctx, &model)
	if err != nil {
		panic(err)
	}
}

type TaskGroupOptions struct {
	Typ       string
	Valid     string
	Cron      string
	ValidDays int64
}

type ModTaskGroupOption func(option *TaskGroupOptions)

//创建task group
func InsertTaskGroup(cate string, name string, desc string, modOption ModTaskGroupOption) int64 {
	option := &TaskGroupOptions{
		Typ:       "N",
		Valid:     "Y",
		Cron:      "",
		ValidDays: -1,
	}

	if modOption != nil {
		modOption(option)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var m TASKMODEL.TaskGroupModel = TASKMODEL.NewTaskGroupModel(conn, c.CacheRedis)

	var model TASKMODEL.TaskGroup = TASKMODEL.TaskGroup{
		Name:      name,
		ValidDays: option.ValidDays,
		Typ:       option.Typ,
		Cron:      option.Cron,
		Desc:      desc,
		Cate:      cate,
		Valid:     option.Valid,
	}

	result, err := m.Insert(ctx, &model)
	if err != nil {
		panic(err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		panic(err)
	}
	return id
}

type TaskOptions struct {
	SortIndex int64
}

type ModTaskOption func(option *TaskOptions)

//创建任务
func InsertTask(groupId int64, name string, desc string, modOption ModTaskOption) int64 {
	option := &TaskOptions{
		SortIndex: 1,
	}

	if modOption != nil {
		modOption(option)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var model TASKMODEL.Task = TASKMODEL.Task{
		Name:      name,
		Desc:      desc,
		GroupId:   groupId,
		SortIndex: option.SortIndex,
	}

	var m TASKMODEL.TaskModel = TASKMODEL.NewTaskModel(conn, c.CacheRedis)

	result, err := m.Insert(ctx, &model)
	if err != nil {
		panic(err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		panic(err)
	}
	return id
}

func InsertTaskCondition(groupId, taskId int64, conditionName string, count int64) int64 {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var model TASKMODEL.TaskCondition = TASKMODEL.TaskCondition{
		TaskId:       taskId,
		TaskCondName: conditionName,
		Count:        count,
		GroupId:      groupId,
	}

	var m TASKMODEL.TaskConditionModel = TASKMODEL.NewTaskConditionModel(conn, c.CacheRedis)
	result, err := m.Insert(ctx, &model)
	if err != nil {
		panic(err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		panic(err)
	}
	return id
}

func InsertTaskBonus(taskId int64, name string, desc string, count int64) int64 {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var model TASKMODEL.TaskBonus = TASKMODEL.TaskBonus{
		TaskId: taskId,
		Name:   name,
		Desc:   desc,
		Count:  count,
	}

	var m TASKMODEL.TaskBonusModel = TASKMODEL.NewTaskBonusModel(conn, c.CacheRedis)
	result, err := m.Insert(ctx, &model)
	if err != nil {
		panic(err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		panic(err)
	}
	return id
}

type taskProgressOptions struct {
	BatchId int64
}

type ModTaskProgressOption func(option *taskProgressOptions)

func InsertTaskProgress(groupId, userId int64, conditionName string, count int64, modOption ModTaskProgressOption) int64 {
	option := &taskProgressOptions{
		BatchId: 1,
	}

	if modOption != nil {
		modOption(option)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var model TASKMODEL.TaskProgress = TASKMODEL.TaskProgress{
		BatchId:      option.BatchId,
		GroupId:      groupId,
		TaskCondName: conditionName,
		Count:        count,
		UserId:       userId,
	}

	var m TASKMODEL.TaskProgressModel = TASKMODEL.NewTaskProgressModel(conn, c.CacheRedis)
	result, err := m.Insert(ctx, &model)
	if err != nil {
		panic(err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		panic(err)
	}
	return id
}

func GetAccountByUserId(userId int64) *ACCOUNTMODEL.Account {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	conn := sqlx.NewMysql(c.Mysql.DataSource)

	var m ACCOUNTMODEL.AccountModel = ACCOUNTMODEL.NewAccountModel(conn, c.CacheRedis)
	a, err := m.FindOne(ctx, userId)
	if err != nil {
		panic(err)
	}

	return a
}
